﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.IO;
using System.Xml.Serialization;

namespace DatabaseSynchronizer {

    public class SettingsService {


        private readonly string _filePath;
        private readonly string _applicationDir;        
        private readonly string _fileName = "DBSSettings.xml";

        public SettingsService() {
            _fileName = "DBSSettings.xml";
            _applicationDir = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) + "\\DatabaseSynchronizer\\";
            _filePath = _applicationDir + _fileName;
        }


        /// <summary>
        /// The method tries to find the settings file on the location. 
        /// If file cannot be read or decrypted, the file is deleted and null is returned, otherwise settings are loaded and returned. 
        /// If the settings do not belong to the passed in user and machine, null is returned.
        /// </summary>
        /// <param name="userName">Logged in user</param>
        /// <param name="machineName">Host machine name</param>
        /// <returns>Settings</returns>
        private Settings LoadSettings(string userName, string machineName) {

            Settings result = null;

            if (File.Exists(_filePath)) {

                StreamReader streamReader = new StreamReader(_filePath);

                if (streamReader.Peek() != -1) {

                    XmlSerializer xmlSerializer = new XmlSerializer(typeof(Settings));

                    try {

                        result = xmlSerializer.Deserialize(streamReader) as Settings;
                        streamReader.Close();
                        DecryptSettings(result);

                        if (!result.LoggedInUser.Equals(userName, StringComparison.CurrentCultureIgnoreCase)
                            || !result.MachineName.Equals(machineName, StringComparison.CurrentCultureIgnoreCase)) {

                                result = null;
                        }

                    }
                    catch {
                        try {
                            File.Delete(_filePath);
                        }
                        catch { }
                    }
                    finally {
                        try {
                            streamReader.Close();
                        }
                        catch { }
                    }
                }
            }

            return result;
        }


        private void EncryptSettings(Settings settings) {

            if (settings != null) {

                if (!string.IsNullOrEmpty(settings.ConnectionString)) {
                    settings.ConnectionString = Utils.EncryptString(settings.ConnectionString);
                }

                if (!string.IsNullOrEmpty(settings.LocalScriptsFolder)) {
                    settings.LocalScriptsFolder = Utils.EncryptString(settings.LocalScriptsFolder);
                }

                if (!string.IsNullOrEmpty(settings.LoggedInUser)) {
                    settings.LoggedInUser = Utils.EncryptString(settings.LoggedInUser);
                }

                if (!string.IsNullOrEmpty(settings.MachineName)) {
                    settings.MachineName = Utils.EncryptString(settings.MachineName);
                }

                if (!string.IsNullOrEmpty(settings.TFSServerURL)) {
                    settings.TFSServerURL = Utils.EncryptString(settings.TFSServerURL);
                }

                if (!string.IsNullOrEmpty(settings.TFSScriptsFolder)) {
                    settings.TFSScriptsFolder = Utils.EncryptString(settings.TFSScriptsFolder);
                }

                if (!string.IsNullOrEmpty(settings.TFSId)) {
                    settings.TFSId = Utils.EncryptString(settings.TFSId);
                }

                if (!string.IsNullOrEmpty(settings.TFSPwd)) {
                    settings.TFSPwd = Utils.EncryptString(settings.TFSPwd);
                }
            }

        }


        private void DecryptSettings(Settings settings) {

            if (settings != null) {

                if (!string.IsNullOrEmpty(settings.ConnectionString)) {
                    settings.ConnectionString = Utils.DecryptString(settings.ConnectionString);
                }

                if (!string.IsNullOrEmpty(settings.LocalScriptsFolder)) {
                    settings.LocalScriptsFolder = Utils.DecryptString(settings.LocalScriptsFolder);
                }

                if (!string.IsNullOrEmpty(settings.LoggedInUser)) {
                    settings.LoggedInUser = Utils.DecryptString(settings.LoggedInUser);
                }

                if (!string.IsNullOrEmpty(settings.MachineName)) {
                    settings.MachineName = Utils.DecryptString(settings.MachineName);
                }

                if (!string.IsNullOrEmpty(settings.TFSServerURL)) {
                    settings.TFSServerURL = Utils.DecryptString(settings.TFSServerURL);
                }

                if (!string.IsNullOrEmpty(settings.TFSScriptsFolder)) {
                    settings.TFSScriptsFolder = Utils.DecryptString(settings.TFSScriptsFolder);
                }

                if (!string.IsNullOrEmpty(settings.TFSId)) {
                    settings.TFSId = Utils.DecryptString(settings.TFSId);
                }

                if (!string.IsNullOrEmpty(settings.TFSPwd)) {
                    settings.TFSPwd = Utils.DecryptString(settings.TFSPwd);
                }
            }

        }


        public Settings GetSettings(string userName, string machineName) {

            var settings = LoadSettings(userName, machineName);

            if (settings == null) {
                settings = new Settings {
                    ConnectionString = string.Empty,
                    Database = Database.SQL,
                    IsTFS = false,
                    LocalScriptsFolder = string.Empty,
                    LoggedInUser = Environment.UserName,
                    MachineName = Environment.MachineName,
                    TFSId = string.Empty,
                    TFSPwd = string.Empty,
                    TFSScriptsFolder = string.Empty,
                    TFSServerURL = string.Empty

                };
            }

            return settings;

        }


        public bool SaveSettings(Settings settings, out string errorMsg) {

            errorMsg = string.Empty;
            bool result = true;

            try {
                
                if (!Directory.Exists(_applicationDir)) {
                    Directory.CreateDirectory(_applicationDir);
                }

                EncryptSettings(settings);
                StreamWriter streamWriter = new StreamWriter(_filePath);
                XmlSerializer xmlSerializer = new XmlSerializer(settings.GetType());

                xmlSerializer.Serialize(streamWriter, settings);
                streamWriter.Flush();
                streamWriter.Close();

                settings = LoadSettings(Environment.UserName, Environment.MachineName);
            }
            catch(Exception ex) {
                errorMsg = ex.Message;
                result = false;
            }

            return result;
        }


        public bool IsValid(Settings settings, out List<string> validations) {

            validations = new List<string>();
            string errorMsg;

            if (string.IsNullOrEmpty(settings.LocalScriptsFolder)) {

                validations.Add("Local scripts folder is not available.");
            }
            else if (!Directory.Exists(settings.LocalScriptsFolder)) {

                validations.Add("Local scripts folder (" + settings.LocalScriptsFolder + ") does not exist.");
            }

            if (string.IsNullOrEmpty(settings.ConnectionString)) {

                validations.Add("A connection string is required.");
            }
            else if (!DBServicesFactory.GetDBService(settings).IsValidConnectionString(out errorMsg)) {

                validations.Add(errorMsg);
            }

            return validations.Count == 0;
        }


        public bool AValidSettingExistsForUser(string userName, string machineName) {

            var settings = LoadSettings(Environment.UserName, Environment.MachineName);
            List<string> temp;

            return settings != null
                && settings.LoggedInUser.ToLower().Equals(userName.ToLower())
                && settings.MachineName.ToLower().Equals(machineName.ToLower())
                && IsValid(settings, out temp);
        }

        public bool DeleteSettings(out string errorMsg) {

            errorMsg = string.Empty;
            try {
                if (File.Exists(_filePath)) {
                    File.Delete(_filePath);
                }
                return true;
            }
            catch (Exception ex) {
                errorMsg = ex.Message;
                return false;
            }
        }
    }
}
